﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using System.IO;
using Genuilder.Tests;

namespace Genuilder.Extensibility.Tests
{
	public class SpyExtension : IExtension
	{
		#region IExtension Members

		public void Execute(ExtensionContext extensionContext)
		{
			if(DeleteFile != null)
				extensionContext.GenItems.First(i => i.Name == DeleteFile).Delete();

			foreach(var item in extensionContext.GenItems)
			{
				foreach(var addChild in _AddChild)
				{
					if(addChild.Item1 == item.Name)
					{
						var newItem = item.Children.CreateNew(addChild.Item2, addChild.Item3);
						Assert.True(newItem.Parent == item);
						Assert.True(item.Children.Contains(newItem));
					}
				}
			}
			if(CreateFile != null)
				extensionContext.GenItems.CreateNew(CreateFile);
			GenItems = extensionContext.GenItems;
			if(Throw != null)
				throw Throw;
		}

		#endregion

		public GenItemRepository GenItems
		{
			get;
			private set;
		}

		public string CreateFile
		{
			get;
			set;
		}

		public Exception Throw
		{
			get;
			set;
		}

		public string DeleteFile
		{
			get;
			set;
		}

		List<Tuple<string, string, SourceType>> _AddChild = new List<Tuple<string, string, SourceType>>();
		public void AddChild(string parent, string child)
		{
			_AddChild.Add(Tuple.Create(parent, child, SourceType.Compile));
		}
		public void AddChild(string parent, string child, SourceType type)
		{
			_AddChild.Add(Tuple.Create(parent, child, type));
		}
	}
	public class TestClass
	{
		[Fact]
		public void CanEnumerateCompileInputFile()
		{
			GenEngine engine = new GenEngine();
			engine.SourceFiles.AddRange(new string[] { "Test.cs", "Test1.cs", "Test2.cs" });
			var spy = engine.Extensions.Add(new SpyExtension());
			engine.Run();
			spy.GenItems.AssertHas("Test.cs", "Test1.cs", "Test2.cs");
		}

		[Fact]
		public void CanSeeModifiedFiles()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test.cs", "Test1.cs", "Test2.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			var result = engine.Run();
			Modify("Test1.cs");

			engine = new GenEngine();
			engine.SourceFiles.AddRange(files);
			engine.StartInfo.RunHistory = result.History;
			spy = engine.Extensions.Add(new SpyExtension());
			engine.Run();
			spy.GenItems.AssertHas(i => i.Modified && i.File.Name == "Test1.cs");
			spy.GenItems.AssertHas(i => !i.Modified && i.File.Name == "Test2.cs");
			spy.GenItems.AssertHas(i => !i.Modified && i.File.Name == "Test.cs");
		}

		[Fact]
		public void CanModifyGenerationDirectory()
		{
			using(var directory = new TestDirectory())
			{
				GenEngine engine = new GenEngine();
				engine.StartInfo.GenerationDirectory = directory.Path;
				var spy = engine.Extensions.Add(new SpyExtension());
				spy.CreateFile = "Hello";
				var spy2 = engine.Extensions.Add(new SpyExtension());
				engine.Run();
				spy2.GenItems.AssertHas(i => i.Name == directory.Path + "\\Hello");
			}
		}

		[Fact]
		public void CanDeleteGenItem()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test.cs", "Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.DeleteFile = "Test1.cs";
			var spy2 = engine.Extensions.Add(new SpyExtension());
			engine.Run().AssertNoError();
			spy2.GenItems.AssertHasNot(i => i.File.Name == "Test1.cs");
			spy2.GenItems.AssertHas(i => i.File.Name == "Test.cs");
		}

		[Fact]
		public void CanAddChildren()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test.cs", "Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.AddChild("Test1.cs", "Test11.cs");
			spy.AddChild("Test1.cs", "Test12.cs");
			var spy2 = engine.Extensions.Add(new SpyExtension());
			engine.Run().AssertNoError();
			spy2.GenItems.AssertHas(i => i.File.Name == "Test.cs");
			spy2.GenItems.AssertHas(i => i.File.Name == "Test11.cs");
			spy2.GenItems.AssertHas(i => i.File.Name == "Test12.cs");
		}

		[Fact]
		public void CanCheckParentAndChildren()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test.cs", "Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.AddChild("Test1.cs", "Test11.cs");
			spy.AddChild("Test11.cs", "Test111.cs");
			spy.AddChild("Test11.cs", "Test112.cs");
			spy.AddChild("Test1.cs", "Test12.cs");
			engine.Run().AssertNoError();
		}

		[Fact]
		public void CanAddRootGenItem()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test.cs", "Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.CreateFile = "Test2.cs";
			var spy2 = engine.Extensions.Add(new SpyExtension());
			engine.Run().AssertNoError();
			spy2.GenItems.AssertHas(i => i.File.Name == "Test.cs");
			spy2.GenItems.AssertHas(i => i.File.Name == "Test1.cs");
			spy2.GenItems.AssertHas(i => i.File.Name == "Test2.cs");
		}

		[Fact]
		public void DeletingParentDeleteChildren()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.AddChild("Test1.cs", "Test11.cs");
			spy.AddChild("Test1.cs", "Test12.cs");
			var spy2 = engine.Extensions.Add(new SpyExtension());
			spy2.DeleteFile = "Test1.cs";
			var spy3 = engine.Extensions.Add(new SpyExtension());

			engine.Run().AssertNoError();
			Assert.Empty(spy3.GenItems);
		}

		[Fact]
		public void GeneratedSourceAreKeptBetweenTwoRun()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("Test1.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.AddChild("Test1.cs", "Test11.cs");
			spy.AddChild("Test1.cs", "Test12.cs");
			var spy2 = engine.Extensions.Add(new SpyExtension());
			var result = engine.Run();

			engine = new GenEngine();
			engine.SourceFiles.AddRange(files);
			engine.StartInfo.RunHistory = result.History;
			spy = engine.Extensions.Add(new SpyExtension());
			spy2 = engine.Extensions.Add(new SpyExtension());
			engine.Run();
			spy.GenItems.AssertHas("Test11.cs", "Test12.cs");
			spy.GenItems.AssertHas(o => o.File.Name == "Test11.cs" && o.Generated);
			spy.GenItems.AssertHas(o => o.File.Name == "Test1.cs" && !o.Generated);
		}

		[Fact]
		public void ExtensionsCanThrow()
		{
			GenEngine engine = new GenEngine();
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.Throw = new Exception();
			var result = engine.Run();
			Assert.NotNull(result.Error);
			Assert.Equal(spy, result.Error.ExtensionContext.Extension);
			Assert.Equal(spy.Throw, result.Error.Exception);
		}

		[Fact]
		public void CanGenerateNoneItemsWithCompileSameName()
		{
			GenEngine engine = new GenEngine();
			var files = CreateFiles("MonTest.cs");
			engine.SourceFiles.AddRange(files);
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.AddChild("MonTest.cs", "MonTest.cs", SourceType.None);
			var result = engine.Run(true);
			engine.Extensions.Clear();

			engine.StartInfo.RunHistory = result.History;
			spy = engine.Extensions.Add(new SpyExtension());
			engine.Run();
			Assert.Equal(2, spy.GenItems.Count());
			spy.GenItems.AssertHas("MonTest.cs", SourceType.Compile);
			spy.GenItems.AssertHas("MonTest.cs", SourceType.None);
		}

		[Fact]
		public void CanCreateGenItemTwice()
		{
			GenEngine engine = new GenEngine();
			var spy = engine.Extensions.Add(new SpyExtension());
			spy.CreateFile = "File.cs";
			var result = engine.Run();
			engine.StartInfo.RunHistory = result.History;
			engine.Run(true);
		}



		private void Modify(string file)
		{
			File.WriteAllText(file, "hello");
		}

		private string[] CreateFiles(params string[] files)
		{
			foreach(var file in files)
			{
				if(File.Exists(file))
					File.Delete(file);
				File.Create(file).Close();
			}
			return files;
		}
		[Fact]
		public void CanSelectFilesInFolder()
		{
			using(var root = new TestDirectory())
			{
				root.CreateFiles("a.txt", "b.txt");
				using(var sub = root.CreateSubDirectory("Directory"))
				{
					sub.CreateFiles("c.txt", "d.txt");
					using(var subsub = sub.CreateSubDirectory("SubDirectory"))
					{
						subsub.CreateFile("e.txt");
						new FileQuery()
							.SelectInDirectory("Directory", false)
								.All()
								.ToQuery().Clone()
							.Fetch(root.EnumerateFiles())
							.AssertContainsOnly("Directory\\c.txt", "Directory\\d.txt");

						new FileQuery()
							.SelectInDirectory("Directory", true)
								.All()
								.ToQuery().Clone()
							.Fetch(root.EnumerateFiles())
							.AssertContainsOnly("Directory\\c.txt", "Directory\\d.txt", "Directory\\SubDirectory\\e.txt");
					}
				}
			}
		}

		[Fact]
		public void CanSelectRecursiveInThisDirectory()
		{
			using(var root = new TestDirectory())
			{
				root.CreateFiles("a.txt", "b.txt");
				using(var sub = root.CreateSubDirectory("Directory"))
				{
					sub.CreateFiles("c.txt", "d.txt");
					using(var subsub = sub.CreateSubDirectory("SubDirectory"))
					{
						subsub.CreateFile("e.txt");
						new FileQuery()
							.SelectInThisDirectory(true)
								.All()
								.ToQuery().Clone()
							.Fetch(root.EnumerateFiles())
							.AssertContainsOnly("a.txt", "b.txt", "Directory\\c.txt", "Directory\\d.txt", "Directory\\SubDirectory\\e.txt");
					}
				}
			}
		}

		[Fact]
		public void CanUseEndWithToSelectFolder()
		{
			using(var root = new TestDirectory())
			{
				root.CreateFiles("a.txt", "b.txt", "c.lol");
				new FileQuery()
							.SelectInThisDirectory()
								.EndWith(".txt")
								.ToQuery().Clone()
							.Fetch(root.EnumerateFiles())
							.AssertContainsOnly("a.txt", "b.txt");

			}
		}

		[Fact]
		public void CanUseLikeToSelectFolder()
		{
			using(var root = new TestDirectory())
			{
				root.CreateFiles("a.txt", "b.txt", "c.lol");
				new FileQuery()
							.SelectInThisDirectory(false)
								.Like(".*.txt")
								.ToQuery().Clone()
							.Fetch(root.EnumerateFiles())
							.AssertContainsOnly("a.txt", "b.txt");

			}
		}



		[Fact]
		public void GenItemCollectionCanGetByName()
		{
			var items = new[] { "a.cs", "b.cs" };
			GenEngine engine = new GenEngine();
			engine.SourceFiles.AddRange(items);
			var extension = engine.Extensions.Add(new SpyExtension());
			engine.Run().AssertNoError();
			Assert.Equal(1, extension.GenItems.GetByNames("a.cs").Count());
			Assert.Equal(0, extension.GenItems.GetByNames("c.cs").Count());
		}
		[Fact]
		public void GenItemCollectionCanGetByPartialName()
		{
			var items = new[] { "toto\\a.cs", "b.cs", "toto\\ac.cs" };
			GenEngine engine = new GenEngine();
			engine.SourceFiles.AddRange(items);
			var extension = engine.Extensions.Add(new SpyExtension());
			engine.Run().AssertNoError();
			Assert.Equal(1, extension.GenItems.GetByNames("a.cs").Count());
			Assert.Equal(0, extension.GenItems.GetByNames("c.cs").Count());
			Assert.Equal(0, extension.GenItems.GetByNames("bac.cs").Count());
		}
	}
}
